#include <user.h>

static __inline int  syscall0(uint32_t func) __attribute__((always_inline));
static __inline void syscall0_v(uint32_t func) __attribute__((always_inline));
static __inline int  syscall1(uint32_t func, uint32_t a) __attribute__((always_inline));
static __inline void syscall1_v(uint32_t func, uint32_t a) __attribute__((always_inline));
static __inline int  syscall2(uint32_t func, uint32_t a, uint32_t b) __attribute__((always_inline));
static __inline void syscall2_v(uint32_t func, uint32_t a, uint32_t b) __attribute__((always_inline));
static __inline int  syscall3(uint32_t func, uint32_t a, uint32_t b, uint32_t c) __attribute__((always_inline));
static __inline void syscall3_v(uint32_t func, uint32_t a, uint32_t b, uint32_t c) __attribute__((always_inline));
static __inline int  syscall4(uint32_t func, uint32_t a, uint32_t b, uint32_t c, uint32_t d) __attribute__((always_inline));
static __inline void syscall4_v(uint32_t func, uint32_t a, uint32_t b, uint32_t c, uint32_t d) __attribute__((always_inline));
static __inline int  syscall5(uint32_t func, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e) __attribute__((always_inline));
static __inline void syscall5_v(uint32_t func, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e) __attribute__((always_inline));

static __inline int
syscall0(uint32_t func)
{
	 int result;
	 __asm __volatile("int %1\nmovl %%eax, %0"
					  : "=m"(result)
					  : "n"(INT_SYSCALL), "a"(func));
	 return result;
}

static __inline void
syscall0_v(uint32_t func)
{
	 __asm __volatile("int %0"
					  :
					  : "n"(INT_SYSCALL), "a"(func));
}

static __inline int
syscall1(uint32_t func, uint32_t a)
{
	 int result;
	 __asm __volatile("int %1\nmovl %%eax, %0"
					  : "=m"(result)
					  : "n"(INT_SYSCALL), "a"(func), "b"(a));
	 return result;
}

static __inline void
syscall1_v(uint32_t func, uint32_t a)
{
	 __asm __volatile("int %0"
					  :
					  : "n"(INT_SYSCALL), "a"(func), "b"(a));
}

static __inline int
syscall2(uint32_t func, uint32_t a, uint32_t b)
{
	 int result;
	 __asm __volatile("int %1\nmovl %%eax, %0"
					  : "=m"(result)
					  : "n"(INT_SYSCALL), "a"(func), "b"(a), "c"(b));
	 return result;
}

static __inline void
syscall2_v(uint32_t func, uint32_t a, uint32_t b)
{
	 __asm __volatile("int %0"
					  :
					  : "n"(INT_SYSCALL), "a"(func), "b"(a), "c"(b));
}

static __inline int
syscall3(uint32_t func, uint32_t a, uint32_t b, uint32_t c)
{
	 int result;
	 __asm __volatile("int %1\nmovl %%eax, %0"
					  : "=m"(result)
					  : "n"(INT_SYSCALL), "a"(func), "b"(a), "c"(b), "d"(c));
	 return result;
}

static __inline void
syscall3_v(uint32_t func, uint32_t a, uint32_t b, uint32_t c)
{
	 __asm __volatile("int %0"
					  :
					  : "n"(INT_SYSCALL), "a"(func), "b"(a), "c"(b), "d"(c));
}

static __inline int
syscall4(uint32_t func, uint32_t a, uint32_t b, uint32_t c, uint32_t d)
{
	 int result;
	 __asm __volatile("int %1\nmovl %%eax, %0"
					  : "=m"(result)
					  : "n"(INT_SYSCALL), "a"(func), "b"(a), "c"(b), "d"(c), "S"(d));
	 return result;
}

static __inline void
syscall4_v(uint32_t func, uint32_t a, uint32_t b, uint32_t c, uint32_t d)
{
	 __asm __volatile("int %0"
					  :
					  : "n"(INT_SYSCALL), "a"(func), "b"(a), "c"(b), "d"(c), "S"(d));
}

static __inline int
syscall5(uint32_t func, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e)
{
	 int result;
	 __asm __volatile("int %1\nmovl %%eax, %0"
					  : "=m"(result)
					  : "n"(INT_SYSCALL), "a"(func), "b"(a), "c"(b), "d"(c), "S"(d), "D"(e));
	 return result;
}

static __inline void
syscall5_v(uint32_t func, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e)
{
	 __asm __volatile("int %0"
					  :
					  : "n"(INT_SYSCALL), "a"(func), "b"(a), "c"(b), "d"(c), "S"(d), "D"(e));
}

/* ============================================================ */

void
kprintf(const char *fmt, ...)
{
     va_list va;
     va_start(va, fmt);
     syscall2_v(SC_KPRINTF, (uint32_t)fmt, (uint32_t)va);
     va_end(va);
}

void
vkprintf(const char *fmt, va_list va)
{
     syscall2_v(SC_KPRINTF, (uint32_t)fmt, (uint32_t)va);
}

void
upriv_set(void *upriv)
{
	 syscall1_v(SC_UPRIV_SET, (uint32_t)upriv);
}

void
kmsg_init(kmsg_dispatcher_f disp)
{
	 syscall1_v(SC_KMSG_INIT, (uint32_t)disp);
}

void
kmsg_return(uint32_t head, int busy)
{
	 syscall2_v(SC_KMSG_RETURN, head, busy);
}

void
kmsg_clean_busy(void)
{
	 syscall0_v(SC_KMSG_CLEAN_BUSY);
}

physaddr_t
ppage_alloc(int num, int dma)
{
	 return syscall2(SC_PPAGE_ALLOC, (uint32_t)num, (uint32_t)dma);
}

int
ppage_free(physaddr_t addr)
{
	 return syscall1(SC_PPAGE_FREE, (uint32_t)addr);
}

physaddr_t
mmap_get(void *addr)
{
	 return syscall1(SC_MMAP_GET, (uint32_t)addr);
}

int
mmap_set(void *addr, physaddr_t paddr)
{
	 return syscall2(SC_MMAP_SET, (uint32_t)addr, (uint32_t)paddr);
}

void
uthread_create(const char *name, uintptr_t entry, uintptr_t upriv, uintptr_t ustack_top)
{
	 syscall4_v(SC_UTHREAD_CREATE, (uint32_t)name, entry, upriv, ustack_top);
}

uint32_t
process_io_buffer(uint32_t *buf, uint32_t count)
{
	 return syscall2(SC_PROCESS_IO_BUFFER, (uint32_t)buf, (uint32_t)count);
}

void
dummy_work(int workset_pages)
{
	 syscall1_v(SC_DUMMY_WORK, workset_pages);
}

void
exam_addr(void *addr)
{
	 syscall1_v(SC_EXAM_ADDR, (uint32_t)addr);
}

void
yield(void)
{
	 syscall0_v(SC_YIELD);
}
